package org.yunai.swjg.server.module.item.vo;

import org.slf4j.Logger;
import org.yunai.swjg.server.core.constants.SysMessageConstants;
import org.yunai.swjg.server.entity.ItemEntity;
import org.yunai.swjg.server.module.idSequence.IdSequenceHolder;
import org.yunai.swjg.server.module.item.ItemDef;
import org.yunai.swjg.server.module.item.container.Bag;
import org.yunai.swjg.server.module.item.container.CommonBag;
import org.yunai.swjg.server.module.item.container.Containable;
import org.yunai.swjg.server.module.item.event.ItemCountChangeEvent;
import org.yunai.swjg.server.module.item.operation.move.ItemMoveOperation;
import org.yunai.swjg.server.module.item.operation.move.ItemMoveOperations;
import org.yunai.swjg.server.module.item.operation.use.ItemUseOperation;
import org.yunai.swjg.server.module.item.operation.use.ItemUseOperations;
import org.yunai.swjg.server.module.item.template.ItemTemplate;
import org.yunai.swjg.server.module.player.vo.Player;
import org.yunai.swjg.server.rpc.message.S_C.S_C_SysMessageReq;
import org.yunai.swjg.server.rpc.struct.StBagItemDel;
import org.yunai.swjg.server.rpc.struct.StBagItemUpdate;
import org.yunai.yfserver.common.LoggerFactory;
import org.yunai.yfserver.event.EventDispatcher;
import org.yunai.yfserver.event.IEvent;
import org.yunai.yfserver.persistence.PersistenceObject;
import org.yunai.yfserver.spring.BeanManager;

import java.util.Comparator;

/**
 * 道具
 * User: yunai
 * Date: 13-4-6
 * Time: 上午11:50
 */
public class Item implements PersistenceObject<Integer, ItemEntity>, Containable {

    private static final Logger LOGGER = LoggerFactory.getLogger(LoggerFactory.Logger.item, Item.class);

    /**
     * 道具跌家属比较器，按叠加数递增
     */
    public static final Comparator<Item> COMPARATOR_OVERLAP = new Comparator<Item>() {

        @Override
        public int compare(Item itemA, Item itemB) {
            int a = itemA.getOverlap();
            int b = itemB.getOverlap();
            return a > b ? 1 : a == b ? 0 : -1;
        }
    };

    /**
     * 道具携带者ID为空时的值
     */
    public static final int OWNER_ID_NONE = 0;


    private static EventDispatcher eventDispatcher;
    private static ItemUseOperations itemUseOperations;
    private static ItemMoveOperations itemMoveOperations;

    static {
        eventDispatcher = BeanManager.getBean(EventDispatcher.class);
        itemUseOperations = BeanManager.getBean(ItemUseOperations.class);
        itemMoveOperations = BeanManager.getBean(ItemMoveOperations.class);
    }

    /**
     * ID
     */
    private int id;
    /**
     * 所属玩家
     */
    private Player player;
    /**
     * 佩带着ID
     */
    private int wearId;
    /**
     * 所属背包类型
     */
    private Bag.BagType bagType;
    /**
     * 物品所在背包位置
     */
    private int bagIndex;
    /**
     * 道具模版
     */
    private ItemTemplate template;
//    /**
//     * TODO 道具实例属性
//     */
//    private ItemFeature feature;
    /**
     * 叠加数量
     */
    private int overlap;
    /**
     * 是否在数据库中
     */
    private boolean inDB;

    private Item() {
    }

    @Override
    public Integer getId() {
        return id;
    }

    @Override
    public Bag.BagType getBagType() {
        return bagType;
    }

    @Override
    public int getIndex() {
        return bagIndex;
    }

    @Override
    public int getOverlap() {
        return overlap;
    }

    @Override
    public int getMaxOverlap() {
        return template.getMaxOverlap();
    }

    public ItemTemplate getTemplate() {
        return template;
    }

    public int getTemplateId() {
        return template.getId();
    }

    public ItemDef.Type getType() {
        return template.getType();
    }

    public boolean canOverlap() {
        return template.canOverlap();
    }

//    public ItemFeature getFeature() {
//        return feature;
//    }

    public Player getPlayer() {
        return player;
    }

    public int getWearId() {
        return wearId;
    }

    @Override
    public boolean isInDB() {
        return inDB;
    }

    @Override
    public void setInDB(boolean inDB) {
        this.inDB = inDB;
    }

    @Override
    public Integer getUnitId() {
        return player.getId();
    }

    /**
     * 判断是否是空物品
     *
     * @param item 物品
     * @return 是否是空物品
     */
    public static boolean isEmpty(Item item) {
        return item == null || item.getOverlap() == 0;
    }

    @Override
    public String toString() {
        return "Item{" +
                "id=" + id +
                ", playerEntity=" + player +
                ", wearId=" + wearId +
                ", bagType=" + bagType +
                ", bagIndex=" + bagIndex +
                ", template=" + template +
                ", overlap=" + overlap +
                ", inDB=" + inDB +
                '}';
    }

    // ==================== 业务方法BEGIN ====================

    /**
     * 创建道具
     *
     * @param entity 道具实体
     * @param player 玩家
     * @return 道具
     */
    public static Item build(ItemEntity entity, Player player) {
        Item item = new Item();
        item.player = player;
        item.fromEntity(entity);
        item.inDB = true;
        return item;
    }

    /**
     * 保存新道具，并将新道具放入背包，最后返回该道具对象
     *
     * @param player   玩家
     * @param wearId   穿戴着
     * @param template 道具模版
     * @param bagType  背包类型
     * @param bagIndex 位置
     * @param overlap  道具数量
     * @return 道具对象
     */
    public static Item save(Player player, int wearId, ItemTemplate template, Bag.BagType bagType,
                            Integer bagIndex, Integer overlap) {
        Item item = new Item();
        item.id = IdSequenceHolder.genItemId();
        item.player = player;
        item.wearId = wearId;
        item.template = template;
        item.bagType = bagType;
        item.bagIndex = bagIndex;
        item.overlap = 0;
        item.inDB = false;
        player.getOnline().getPlayer().getInventory().getBag(wearId, bagType).putItem(item);
        // 修改数据
        item.changeOverlap(overlap);
        return item;
    }

    /**
     * 创建一个被用于生成移除消息{@link StBagItemDel}的道具对象
     *
     * @param item 道具
     * @return 移除道具。为了防止混淆，该对象的命名最好叫msgDelItem
     */
    public static Item buildRemoveItem(Item item) {
        Item removeItem = new Item();
        removeItem.bagType = item.getBagType();
        removeItem.bagIndex = item.getIndex();
        removeItem.wearId = item.getWearId();
        removeItem.overlap = 0;
        return removeItem;
    }

    public void changeIndex(final int newIndex) {
        this.changeBagType(bagType, newIndex);
    }

    public void changeBagType(Bag.BagType newBagType) {
        this.changeBagType(newBagType, bagIndex);
    }

    public void changeBagType(Bag.BagType newBagType, final int newIndex) {
        if (bagType == newBagType && newIndex == bagIndex) {
            return;
        }
        bagType = newBagType;
        bagIndex = newIndex;
        player.save(this);
    }

    /**
     * 修改道具携带者
     *
     * @param newOwnerId 新携带者，可以为null
     */
    public void changeOwner(int newOwnerId) {
        if (wearId != newOwnerId) {
            wearId = newOwnerId;
            player.save(this);
        }
    }

    /**
     * @return 是否是装备
     */
    public boolean isEquipment() {
        return this.getTemplate().getIdentityType() == ItemDef.IdentityType.EQUIPMENT;
    }

    /**
     * 修改道具数量
     *
     * @param newOverlap 道具新数量
     */
    public void changeOverlap(final int newOverlap) {
        if (newOverlap == overlap) {
            return;
        }
        final int oldOverlap = overlap;
        this.overlap = normalizeOverlap(newOverlap);
        if (overlap > 0) {
            // TODO 等有日志服务器
            player.save(this);
        } else if (overlap == 0) {
            // TODO 等有日志服务器
            player.getInventory().getBag(wearId, bagType).removeItem(bagIndex);
            player.delete(this);
        } else {
            throw new IllegalArgumentException("The overlap must not be <0");
        }
        // 触发物品数量变化事件
        IEvent event = new ItemCountChangeEvent(player, this, oldOverlap);
        eventDispatcher.fireEvent(event);
    }

    private int normalizeOverlap(int overlap) {
        int normalized;
        boolean ok = true;
        if (overlap < 0) {
            normalized = 0;
            ok = false;
        } else if (overlap > getMaxOverlap()) {
            normalized = getMaxOverlap();
            ok = false;
        } else {
            normalized = overlap;
        }
        if (!ok) {
            LOGGER.error("[normalizeOverlap] [非法的叠加数 id:{} overlap:{}]", id, overlap);
        }
        return normalized;
    }

    @Override
    public void fromEntity(ItemEntity entity) {
        this.id = entity.getId();
        this.wearId = entity.getWearId();
        this.bagIndex = entity.getBagIndex();
        this.overlap = entity.getOverlap();
        this.bagType = Bag.BagType.valueOf((byte) entity.getBagId());
        this.template = ItemTemplate.getItemTemplate(entity.getTemplateId());
//        if (entity.getProperties() != null && entity.getProperties().length() > 0) {
//            switch (this.template.getIdentityType()) {
//                case EQUIPMENT:
//                    this.feature = JSON.parseObject(entity.getProperties(), EquipFeature.class);
//                    break;
//            }
//        }
    }

    @Override
    public ItemEntity toEntity() {
        ItemEntity entity = new ItemEntity();
        entity.setId(this.id);
        entity.setPlayerId(this.player.getId());
        entity.setWearId(this.wearId);
        entity.setBagIndex(this.bagIndex);
        entity.setOverlap(this.overlap);
        entity.setBagId(this.bagType.getIndex());
        entity.setTemplateId(this.template.getId());
//        switch (this.template.getIdentityType()) {
//            case EQUIPMENT:
//                entity.setProperties(JSON.toJSONString(this.feature));
//                break;
//        }
        return entity;
    }

    public void use(int targetId, int param) {
        if (Item.isEmpty(this)) {
            player.message(new S_C_SysMessageReq(SysMessageConstants.ITEM_NOT_FOUND));
            return;
        }
        if (!getTemplate().isCanUse()) {
            player.message(new S_C_SysMessageReq(SysMessageConstants.ITEM_CAN_NOT_USE));
            return;
        }
        // 只可以使用主背包、装备包里的道具 TODO 以后加个命格背包
        if (bagType == null || (bagType != Bag.BagType.PRIM && bagType != Bag.BagType.EQUIPMENT)) {
            player.message(new S_C_SysMessageReq(SysMessageConstants.ITEM_CAN_NOT_USE));
            return;
        }
        ItemUseOperation operation = itemUseOperations.getSuitableOperation(player, this, targetId);
        if (operation == null) {
            player.message(new S_C_SysMessageReq(SysMessageConstants.ITEM_CAN_NOT_USE));
            return;
        }
        // 执行
        operation.use(player, this, targetId, param);
    }

    public void move(CommonBag toBag, int toIndex) {
        ItemMoveOperation operation = itemMoveOperations.getSuitableOperation(player, this, toBag, toIndex);
        if (operation == null) {
            player.message(new S_C_SysMessageReq(SysMessageConstants.ITEM_MOVE_FAIL));
            return;
        }
        operation.move(player, this, toBag, toIndex);
    }

    /**
     * 判断是否在当前位置
     *
     * @param bagType  背包类型
     * @param bagIndex 背包位置
     * @return 是否在该位置
     */
    public boolean isAtThisSlot(Bag.BagType bagType, int bagIndex) {
        return this.bagType == bagType && this.bagIndex == bagIndex;
    }

    public StBagItemUpdate genStBagItemUpdate() {
        return new StBagItemUpdate(bagType.get(), (byte) bagIndex, id, overlap, template.getId(), wearId);
    }

    public StBagItemDel genStBagItemDel() {
        return new StBagItemDel(bagType.get(), (byte) bagIndex, wearId);
    }

    /**
     * @return 道具所在背包
     */
    public CommonBag getBag() {
        return player.getInventory().getBag(wearId, bagType);
    }
}